/*
 * Project: a2
 * Copyright (C) 2008 ralfoide gmail com,
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


package com.alfray.a2.gameplay;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.OrientationListener;
import android.view.View;
import android.widget.TextView;

import com.alfray.a2.AboutActivity;
import com.alfray.a2.PrefsActivity;
import com.alfray.a2.PrefsValues;
import com.alfray.a2.R;
import com.alfray.a2.engine.GLView;
import com.alfray.a2.engine.SpriteRenderer;
import com.alfray.a2.engine.UIEventAdapter;


public class GameActivity extends Activity {

    private static final String TAG = "A2GameActivity";
    private static final boolean DEBUG = true;

    private static final long TEXT_UPDATE_DELAY_MS = 1000; /*1s*/

    private static final int PREFS_UPDATED = 42;

    private GLView mGLView;
    private GameState mGameState;
    private GameContext mGameContext;
    private View mDirIcon;
    private TextView mText;
    private TextUpdater mTextUpdater;
    private boolean mIsPaused;
    private Gameplay mGameplay;
    private AnimThread mAnimThread;
    private ActionThread mActionThread;
    private UIEventAdapter mEventAdapter;
    private MyOrientationListener mOrienteer;
    private BitmapDrawable[] mDirBitmaps = new BitmapDrawable[4];
    private PrefsValues mPrefsValues;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        setContentView(R.layout.game);

        mIsPaused = true;
        mGameState = new GameState();
        mGameContext = new GameContext();
        mGameplay = new Gameplay(mGameState);
        mActionThread = new ActionThread(mGameplay, mGameState);
        mAnimThread = new AnimThread(mGameState);
        mEventAdapter = new UIEventAdapter(mGameState, mGameplay);
        mOrienteer = new MyOrientationListener(this);
        mGameplay.setAnimThread(mAnimThread);
        mGameplay.setActionThread(mActionThread);

        mGLView = (GLView) findViewById(R.id.glview);
        mGLView.setRenderer(new SpriteRenderer(mGameContext));
        mGLView.setEventAdapter(mEventAdapter);
        mGLView.requestFocus();

        mText = (TextView) findViewById(R.id.text);
        mTextUpdater = new TextUpdater();

        mDirIcon = (View) findViewById(R.id.dir_icon);

        mPrefsValues = new PrefsValues();
        mPrefsValues.update(this);
        mGameContext.setNewVisualTheme(mPrefsValues.getVisualTheme());

        mGameplay.resetBoard(); // TODO restore state

        prepareBitmaps();
    }

    @Override
    protected void onStart() {
        if (DEBUG) Log.d(TAG, "onStart");
        super.onStart();
        mGameplay.start();
    }

    @Override
    protected void onResume() {
        if (DEBUG) Log.d(TAG, "onResume");
        super.onResume();
        mGLView.onResume();
        mAnimThread.start();
        mActionThread.start();
        mOrienteer.enable();
        mIsPaused = false;

        mText.postDelayed(mTextUpdater, TEXT_UPDATE_DELAY_MS);
    }

    @Override
    protected void onPause() {
        if (DEBUG) Log.d(TAG, "onPause");
        super.onPause();
        mIsPaused = true;
        mOrienteer.disable();
        mGLView.onPause();
        mActionThread.onPause();
        mAnimThread.onPause();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        mActionThread.waitForStop();
        mAnimThread.waitForStop();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        // Pass. We asked for KeyboardHidden + Orientation so that we can ignore them.
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == PREFS_UPDATED) {
            mPrefsValues.update(this);
            mGameContext.setNewVisualTheme(mPrefsValues.getVisualTheme());

        }
        super.onActivityResult(requestCode, resultCode, data);
    }

    // --------------------------
    // Options menu

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        menu.add(0, R.string.help,  0, R.string.help).setIcon(R.drawable.ic_menu_help);
        menu.add(0, R.string.settings,  0, R.string.settings).setIcon(R.drawable.ic_menu_preferences);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch(item.getItemId()) {
        case R.string.settings:
            onPrefs();
            break;
        case R.string.help:
            onHelp();
            break;
        }
        return super.onOptionsItemSelected(item);
    }

    // --------------------------

    private void onHelp() {
        Intent intent = new Intent(this, AboutActivity.class);
        startActivity(intent);
    }

    private void onPrefs() {
        Intent intent = new Intent(this, PrefsActivity.class);
        startActivityForResult(intent, PREFS_UPDATED);
    }

    //----

    private void prepareBitmaps() {
        int resId = R.drawable.dir_down;

        try {
            Options opts = new Options();
            opts.inDither = false;
            opts.inPreferredConfig = Config.ARGB_4444;
            Bitmap b = BitmapFactory.decodeResource(getResources(),
                                                    resId, opts);

            int w = b.getWidth();
            int h = b.getHeight();

            BitmapDrawable bd = new BitmapDrawable(b);
            mDirBitmaps[0] = bd;

            Matrix m = new Matrix();
            for (int i = 1; i < 4; i++) {
                Bitmap b2 = Bitmap.createBitmap(w, h, b.getConfig());
                Canvas c = new Canvas(b2);
                m.setRotate(-90 * i, w/2, h/2);
                c.drawBitmap(b, m, null);

                bd = new BitmapDrawable(b2);
                mDirBitmaps[i] = bd;
            }

        } catch(Throwable t) {
            Log.e(TAG, String.format("Failed to decode drawable %d", resId), t);
        }
    }


    //----

    public GameState getGameState() {
        return mGameState;
    }

    private class TextUpdater implements Runnable {
        public void run() {
            if (!mIsPaused) {
                try {
                    String s = String.format(
                            "Score: %1$d, Moves: %2$d -- %3$3.2f fps",
                            mGameState.mScore,
                            mGameState.mMoves,
                            mGLView.getEstimatedFps());
                    mText.setText(s);
                } catch (Throwable t) {
                    Log.d(TAG, "TextUpdater failed", t);
                }
            }
            if (!mIsPaused) {
                mText.postDelayed(mTextUpdater, TEXT_UPDATE_DELAY_MS);
            }
        }
    }

    //----

    /**
     * GameContext contains references to android stuff for the current
     * instance such as resources and views. It must change when the
     * activity changes.
     */
    public class GameContext {

        public String mNewVisualTheme;

        public GameContext() {
            mGameState.setSpriteBgDefault(0);
            mGameState.setSpriteBgSelected(1);
            mGameState.setSpriteMain(2);
        }

        public GameState getState() {
            return mGameState;
        }

        public void setNewVisualTheme(String newVisualTheme) {
            mNewVisualTheme = newVisualTheme;
        }

        public Bitmap[] getThemeBitmaps() {

            String theme = mNewVisualTheme;
            mGameState.setVisualTheme(theme);

            Resources r = getResources();
            int i1 = r.getIdentifier(theme + "_background",  "drawable", "com.alfray.a2");
            int i2 = r.getIdentifier(theme + "_bg_selected", "drawable", "com.alfray.a2");
            int i3 = r.getIdentifier(theme + "_white",       "drawable", "com.alfray.a2");

            return new Bitmap[] {
                    getBitmap(i1),
                    getBitmap(i2),
                    getBitmap(i3)
            };
        }

        private Bitmap getBitmap(int resId) {
            try {
                Options opts = new Options();
                opts.inDither = false;
                opts.inPreferredConfig = Config.ARGB_4444;
                Bitmap b = BitmapFactory.decodeResource(getResources(), resId, opts);

                int w = b.getWidth();
                int h = b.getHeight();
                if (w != Integer.lowestOneBit(w) || h != Integer.lowestOneBit(h)) {
                    Log.e(TAG, String.format("Bitmap drawable/%d is not a power-of-2: %dx%d",
                                              resId, w, h));
                }

                Config c = b.getConfig();
                if (c != Config.ARGB_4444) {
                    int p[] = new int[w*h];
                    b.getPixels(p, 0 /*offset*/, w /*stride*/, 0 /*x*/, 0 /*y*/, w, h);
                    Bitmap b2 = Bitmap.createBitmap(p, w, h, Config.ARGB_4444);
                    b.recycle();
                    b = b2;
                }

                return b;

            } catch(Throwable t) {
                Log.e(TAG, String.format("Failed to decode drawable %d", resId), t);
            }
            return null;
        }
    }

    // -----

    public class MyOrientationListener extends OrientationListener {

        public MyOrientationListener(Context context) {
            super(context);
        }

        @Override
        public void onOrientationChanged(int angle) {
            // snap orientation to 45 angles with a 5 degres margin
            for (int i = 45; i < 360; i += 45) {
                if (angle >= i - 5 && angle <= i + 5) {
                    angle = i;
                    break;
                }
            }
            if (angle <= 5 || angle >= 360 - 5) angle = 0;

            int dir = GameState.DIR_DOWN;
            if (angle < 360-45) {
                if (angle >   0+45) dir++; // DIR_RIGHT
                if (angle >  90+45) dir++; // DIR_UP
                if (angle > 180+45) dir++; // DIR_LEFT
            }

            BitmapDrawable bd = mDirBitmaps[dir];
            if (mDirIcon.getBackground() != bd) {
                mDirIcon.setBackgroundDrawable(bd);
            }

            mGameplay.setScreenAngle(angle, dir);
        }

    }
}
